#coding=utf-8
import sys
from os.path import abspath, dirname, join

import time,os,sys
from Crypto.Cipher import DES3
import datetime,time,hashlib,uuid, base64, random, requests
import gzip

from xml.dom.minidom import Document, parseString
from xml.sax.saxutils import escape
from db.models import InvoiceItem

def getPassWord(passWord):
    #10位随机数+Base64({（10位随机数+注册码）MD5})
    r10 = []
    for i in range(0, 10):
        r10.append(str(random.randint(0,9)))
    r10 = ''.join(r10)
    #m2 = hashlib.md5(r10+passWord).hexdigest()
    return r10 + base64.b64encode(hashlib.md5((r10+passWord).encode('utf-8')).digest()).decode('utf-8')


def pad(text):
    block_size = 8
    count = len(text.encode('utf-8'))
    add = block_size - (count % block_size)
    entext = text + (chr(add) * add)
    return entext.encode('utf-8')

unpad = lambda s: s[0:-ord(s[-1])]
def pkcs5_unpadding(data):
    pad_len = ord(data[-1])
    data = data[:-pad_len]
    return data   
    
#####################################################
#外层报文
######################################################
def appendXmlTextNode(doc, root, name, text):
    element = doc.createElement(name)
    textNode = doc.createTextNode('%s' %(text if text is not None else ''))
    element.appendChild(textNode)
    root.appendChild(element)

def getText(row, nodeName):
    try:
        node = row.getElementsByTagName(nodeName)[0]
        nodelist = node.childNodes
        rc = []
        for node in nodelist:
            if node.nodeType == node.TEXT_NODE:
                rc.append(node.data)
        return ''.join(rc)
    except:
        return ''
        
def toprettyxml_fixed(node, encoding='utf-8'):
    from xml.dom.minidom import parseString
    dom = parseString(node) 
    return dom.toprettyxml(indent='  ')
    
    
def makeOuterXmlNew(app, data={}, pretty=0):
    doc = Document()
    root = doc.createElement('interface')
    doc.appendChild(root)
    root.setAttribute('xmlns','')
    root.setAttribute('xmlns:xsi','http://www.w3.org/2001/XMLSchema-instance')
    root.setAttribute('xsi:schemaLocation','http://www.chinatax.gov.cn/tirip/dataspec/interfaces.xsd')
    root.setAttribute('version','DZFP1.0')
    
    ##################
    globalInfo = doc.createElement('globalInfo')
    root.appendChild(globalInfo)
    appendXmlTextNode(doc, globalInfo, 'terminalCode', '0')
    appendXmlTextNode(doc, globalInfo, 'appId', 'DZFP')
    appendXmlTextNode(doc, globalInfo, 'version', '2.0')
    appendXmlTextNode(doc, globalInfo, 'interfaceCode', data.get('interfaceCode', ''))
    appendXmlTextNode(doc, globalInfo, 'requestCode', app.userName)
    appendXmlTextNode(doc, globalInfo, 'requestTime', '%s' %datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
    appendXmlTextNode(doc, globalInfo, 'responseCode', '') #???
    appendXmlTextNode(doc, globalInfo, 'dataExchangeId', data.get('dataExchangeId', ''))
    #appendXmlTextNode(doc, globalInfo, 'dataExchangeId', data.get('dataExchangeId', 'P0000001ECXML.FPKJ.BC.E_INV20290202eXl4EymmJ'))
    appendXmlTextNode(doc, globalInfo, 'userName', app.userName)
    appendXmlTextNode(doc, globalInfo, 'passWord', data.get('passWord', ''))
    appendXmlTextNode(doc, globalInfo, 'taxpayerId', app.XHF_NSRSBH)
    appendXmlTextNode(doc, globalInfo, 'authorizationCode', app.authorizationCode)
    
    #################
    returnStateInfo = doc.createElement('returnStateInfo')
    root.appendChild(returnStateInfo)
    appendXmlTextNode(doc, returnStateInfo, 'returnCode', '')
    appendXmlTextNode(doc, returnStateInfo, 'returnMessage', '')
    
    #################
    Data = doc.createElement('Data')
    root.appendChild(Data)
    dataDescription = doc.createElement('dataDescription')
    Data.appendChild(dataDescription)
    appendXmlTextNode(doc, dataDescription, 'zipCode', '0')
    appendXmlTextNode(doc, dataDescription, 'encryptCode', '1')
    appendXmlTextNode(doc, dataDescription, 'codeType', '3DES')
    appendXmlTextNode(doc, Data, 'content', data.get('content')) #base64请求数据内容或返回数据内容
    
    if pretty:
        
        xmlStr = toprettyxml_fixed(doc, encoding='utf-8')
    else:
        xmlStr = doc.toxml(encoding="utf-8")
    return xmlStr
        
def makeOuterXml(app, data={}, pretty=0):
    doc = Document()
    root = doc.createElement('interface')
    doc.appendChild(root)
    root.setAttribute('xmlns','')
    root.setAttribute('xmlns:xsi','http://www.w3.org/2001/XMLSchema-instance')
    root.setAttribute('xsi:schemaLocation','http://www.chinatax.gov.cn/tirip/dataspec/interfaces.xsd')
    root.setAttribute('version','DZFP1.0')
    
    ##################
    globalInfo = doc.createElement('globalInfo')
    root.appendChild(globalInfo)
    appendXmlTextNode(doc, globalInfo, 'terminalCode', '0')
    appendXmlTextNode(doc, globalInfo, 'appId', 'ZZS_PT_DZFP')
    appendXmlTextNode(doc, globalInfo, 'version', '2.0')
    appendXmlTextNode(doc, globalInfo, 'interfaceCode', data.get('interfaceCode', ''))
    appendXmlTextNode(doc, globalInfo, 'requestCode', app.userName)
    appendXmlTextNode(doc, globalInfo, 'requestTime', '%s' %datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
    appendXmlTextNode(doc, globalInfo, 'responseCode', '') #???
    appendXmlTextNode(doc, globalInfo, 'dataExchangeId', data.get('dataExchangeId', ''))
    #appendXmlTextNode(doc, globalInfo, 'dataExchangeId', data.get('dataExchangeId', 'P0000001ECXML.FPKJ.BC.E_INV20290202eXl4EymmJ'))
    appendXmlTextNode(doc, globalInfo, 'userName', app.userName)
    appendXmlTextNode(doc, globalInfo, 'passWord', data.get('passWord', ''))
    appendXmlTextNode(doc, globalInfo, 'taxpayerId', app.XHF_NSRSBH)
    appendXmlTextNode(doc, globalInfo, 'authorizationCode', app.authorizationCode)
    
    #################
    returnStateInfo = doc.createElement('returnStateInfo')
    root.appendChild(returnStateInfo)
    appendXmlTextNode(doc, returnStateInfo, 'returnCode', '')
    appendXmlTextNode(doc, returnStateInfo, 'returnMessage', '')
    
    #################
    Data = doc.createElement('Data')
    root.appendChild(Data)
    dataDescription = doc.createElement('dataDescription')
    Data.appendChild(dataDescription)
    appendXmlTextNode(doc, dataDescription, 'zipCode', '0')
    appendXmlTextNode(doc, dataDescription, 'encryptCode', '1')
    appendXmlTextNode(doc, dataDescription, 'codeType', '3DES')
    appendXmlTextNode(doc, Data, 'content', data.get('content')) #base64请求数据内容或返回数据内容
    
    if pretty:
        #xmlStr = doc.toprettyxml(indent='  ', newl='\n', encoding="utf-8")
        xmlStr = toprettyxml_fixed(doc, encoding='utf-8')
    else:
        xmlStr = doc.toxml(encoding="utf-8")
    return xmlStr

######################################################
#发票开具
######################################################
def escapeXmlField(val):
    if val is None:
        return ''
    return escape(val)

def fpkjInxml(app, kpsq):
    return fpkjInxml_public(app, kpsq)
    
def fpkjInxml_public(app, kpsq):
    kpsqGoodss = InvoiceItem.objects.filter(invoice=kpsq)
    
    CZDM = 10
    BZ = '' if kpsq.BZ is None else kpsq.BZ
    FPQQLSH = (kpsq.FPQQLSH).rjust(20,'0')
    
    inXml = '''<REQUEST_FPKJXX class="REQUEST_FPKJXX"><FPKJXX_FPTXX class="FPKJXX_FPTXX">'''
    inXml += '<FPQQLSH>%s</FPQQLSH>' %FPQQLSH #发票请求唯一流水号
    inXml += '<DSPTBM>%s</DSPTBM>' %app.userName #平台编码
    inXml += '<NSRSBH>%s</NSRSBH>' %kpsq.XHF_NSRSBH #开票方识别号
    inXml += '<NSRMC>%s</NSRMC>' %escapeXmlField(kpsq.XHFMC) #开票方名称
    inXml += '<NSRDZDAH></NSRDZDAH>' #开票方电子档案号
    inXml += '<SWJG_DM></SWJG_DM>' #税务机构代码
    inXml += '<DKBZ>0</DKBZ>' #代开标志

    inXml += '<PYDM></PYDM>' #票样代码
    inXml += '<KPXM>商品明细</KPXM>' #主要开票项目 主要开票商品，或者第一条商品，取项目信息中第一条数据的项目名称（或传递大类例如：办公用品） 
    inXml += '<BMB_BBH>12.0</BMB_BBH>' #编码表版本号
    inXml += '<XHF_NSRSBH>%s</XHF_NSRSBH>' %kpsq.XHF_NSRSBH #销货方识别号
    inXml += '<XHFMC>%s</XHFMC>' %escapeXmlField(kpsq.XHFMC) #销货方名称
    inXml += '<XHF_DZ>%s</XHF_DZ>' %escapeXmlField(kpsq.XHF_DZ) #销货方地址
    inXml += '<XHF_DH>%s</XHF_DH>' %escapeXmlField(kpsq.XHF_DH) #销货方电话
    inXml += '<XHF_YHZH>%s</XHF_YHZH>' %escapeXmlField(kpsq.XHF_YHZH) #销货方银行账号
    inXml += '<GHFMC>%s</GHFMC>' %escapeXmlField(kpsq.GHFMC) #购货方名称
    inXml += '<GHF_NSRSBH>%s</GHF_NSRSBH>' %(kpsq.GHF_NSRSBH if kpsq.GHF_NSRSBH is not None else '') #购货方识别号
    inXml += '<GHF_SF>%s</GHF_SF>' %escapeXmlField(kpsq.GHF_SF) #购货方省份
    inXml += '<GHF_DZ>%s</GHF_DZ>' %escapeXmlField(kpsq.GHF_DZ) #购货方地址
    inXml += '<GHF_GDDH>%s</GHF_GDDH>' %escapeXmlField(kpsq.GHF_GDDH) #购货方固定电话
    inXml += '<GHF_SJ>%s</GHF_SJ>' %escapeXmlField(kpsq.GHF_SJ) #购货方手机
    inXml += '<GHF_EMAIL>%s</GHF_EMAIL>' %(escapeXmlField(kpsq.GHF_EMAIL[:50]) if kpsq.GHF_EMAIL is not None and app.emailSendBy==0 else '') #购货方邮箱
    inXml += '<GHFQYLX>01</GHFQYLX>'
    inXml += '<GHF_YHZH>%s</GHF_YHZH>' %escapeXmlField(kpsq.GHF_YHZH) #购货方银行、账号
    inXml += '<HY_DM></HY_DM>' #行业代码
    inXml += '<HY_MC></HY_MC>' #行业名称
    inXml += '<KPY>%s</KPY>' %escapeXmlField(kpsq.KPY) #开票员
    inXml += '<SKY>%s</SKY>' %escapeXmlField(kpsq.SKY) if kpsq.InfoKind not in [61,62] else ''#收款员
    inXml += '<FHR>%s</FHR>' %escapeXmlField(kpsq.FHR) if kpsq.InfoKind not in [61,62] else ''#复核人
    inXml += '<KPRQ></KPRQ>' #开票日期
    inXml += '<KPLX>%s</KPLX>' %kpsq.KPLX #开票类型 1	正票 2	红票  
    inXml += '<FPLX>%s</FPLX>' %kpsq.InfoKind #20221208
    inXml += '<YFP_DM>%s</YFP_DM>' %(kpsq.YFP_DM if kpsq.YFP_DM else '') #原发票代码
    inXml += '<YFP_HM>%s</YFP_HM>' %(kpsq.YFP_HM if kpsq.YFP_HM else '') #原发票号码
    inXml += '<CZDM>%s</CZDM>' %CZDM #操作代码 10正票正常开具 20退货折让红票 
    inXml += '<QD_BZ>0</QD_BZ>' #清单标志 默认为0
    inXml += '<QDXMMC></QDXMMC>' #清单发票项目名称
    inXml += '<CHYY>%s</CHYY>' %('2' if kpsq.KPLX in [2, '2'] else '') #冲红原因
    inXml += '<TSCHBZ></TSCHBZ>' #特殊冲红标志
    inXml += '<KPHJJE>%.02f</KPHJJE>' %kpsq.KPHJJE #价税合计金额
    inXml += '<HJBHSJE>%.02f</HJBHSJE>' %kpsq.HJBHSJE #合计不含税金额
    inXml += '<HJSE>%.02f</HJSE>' %kpsq.HJSE #合计税额
    inXml += '<BZ>%s</BZ>'%escapeXmlField(BZ) if kpsq.KPLX not in [2,'2'] else '' #备注 #备注

    #特殊票种使用jsonobj冲突写删除所有逻辑
    #if BDCZL:
    #    inXml += '<TDYS>06</TDYS>'
    

    inXml += '<BYZD1></BYZD1>'
    inXml += '<BYZD2></BYZD2>'
    inXml += '<BYZD3></BYZD3>'
    inXml += '<BYZD4></BYZD4>'
    inXml += '<BYZD5></BYZD5>'
    inXml += '</FPKJXX_FPTXX>'

    kpsqGoodsCnt = 0
    for kpsqGoods in kpsqGoodss:
        if kpsqGoods.XMJE == 0:
            continue
        kpsqGoodsCnt += 1

    
    inXml += '<FPKJXX_XMXXS class="FPKJXX_XMXX;" size="%s">' %kpsqGoodsCnt
    for kpsqGoods in kpsqGoodss:
        if kpsqGoods.XMJE == 0:
            continue
            
        
        
        inXml += '<FPKJXX_XMXX>'
        inXml += '<XMMC>%s</XMMC>' %escapeXmlField(kpsqGoods.XMMC) #项目名称
        inXml += '<XMDW>%s</XMDW>' %escapeXmlField(kpsqGoods.XMDW) #项目单位
        inXml += '<GGXH>%s</GGXH>' %escapeXmlField(kpsqGoods.GGXH) #规格型号
        inXml += '<XMSL>%s</XMSL>' %(kpsqGoods.XMSL if kpsqGoods.XMSL else '') #项目数量
        inXml += '<HSBZ>%s</HSBZ>' %1  #(0 if kpsqGoods.HSBZ in [None, 0, '0'] else 1 ) #含税标志
        inXml += '<FPHXZ>%s</FPHXZ>' %kpsqGoods.FPHXZ #发票行性质
        inXml += '<XMDJ>%s</XMDJ>' %(kpsqGoods.XMDJ_HS if kpsqGoods.XMDJ_HS else '')  #项目单价
        #inXml += '<XMDJ></XMDJ>'
        inXml += '<XMJE>%s</XMJE>' %round(kpsqGoods.XMJE_HS, 2) #项目金额
        inXml += '<SPBM>%s</SPBM>' %kpsqGoods.SPBM #商品编码
        inXml += '<ZXBM></ZXBM>'
        inXml += '<YHZCBS>%s</YHZCBS>' %(1 if kpsqGoods.YHZCBS == '1' else 0) #优惠政策标识
        inXml += '<LSLBS>%s</LSLBS>' %(3 if kpsqGoods.SL == 0 else '')#(kpsqGoods.LSLBS) #零税率标识
        inXml += '<ZZSTSGL>%s</ZZSTSGL>' %kpsqGoods.ZZSTSGL
        inXml += '<KCE>%s</KCE>' %(kpsqGoods.KCE if kpsqGoods.KCE not in [None, ''] else '0') #扣除额
        inXml += '<SL>%s</SL>' %kpsqGoods.SL #税率
        inXml += '<SE>%s</SE>' %(kpsqGoods.SE if kpsqGoods.SE else '') #税额
        inXml += '<BYZD1></BYZD1>'
        inXml += '<BYZD2></BYZD2>'
        inXml += '<BYZD3></BYZD3>'
        inXml += '<BYZD4></BYZD4>'
        inXml += '<BYZD5></BYZD5>'
        inXml += '</FPKJXX_XMXX>'
    inXml += '</FPKJXX_XMXXS>'
    inXml += '''<FPKJXX_DDXX class="FPKJXX_DDXX">
		<DDH>%s</DDH>
		<THDH></THDH>
		<DDDATE />
	</FPKJXX_DDXX>
</REQUEST_FPKJXX>''' %FPQQLSH
    inXml = inXml.encode('gbk', 'ignore').decode('gbk', 'ignore')
    return inXml
    
    
def fpqdredInxml(app, kpsq):
    kpsqGoodss = InvoiceItem.objects.filter(invoice=kpsq)
    BZ = '' if kpsq.BZ is None else kpsq.BZ
    for kpsqGoods in kpsqGoodss:
        if kpsqGoods.YHZCBS in ['1', 1] and kpsqGoods.ZZSTSGL == '按5%简易征收减按1.5%计征':
            BZ = '按5%简易征收减按1.5%计征 ' + BZ
    #20220111 end        
    
    BDCZL = False #不动产租赁
    for kpsqGoods in kpsqGoodss:
        if kpsqGoods.SPBM == '3040502029902000000' and kpsq.InfoKind in [61, 62]:
            BDCZL = True
    
    #20230313 部分红冲
    CZDM = 10
    #20230313 end
        
    FPQQLSH = (kpsq.FPQQLSH).rjust(20,'0')
    
    inXml = '''<REQUEST_QDHZSQ class="REQUEST_QDHZSQ">'''
    inXml += '<QQLSH>%s</QQLSH>' %FPQQLSH #发票请求唯一流水号
    #inXml += '<DSPTBM>%s</DSPTBM>' %app.userName #平台编码
    inXml += '<NSRSBH>%s</NSRSBH>' %kpsq.XHF_NSRSBH #开票方识别号
    inXml += '<LRFSF>01</LRFSF>' #01：销方；02：购方
    inXml += '<XSFNSRSBH>%s</XSFNSRSBH>' %kpsq.XHF_NSRSBH #销售方纳税人识别号
    inXml += '<XSFMC>%s</XSFMC>' %escapeXmlField(kpsq.XHFMC) #开票方名称
    inXml += '<GMFNSRSBH>%s</GMFNSRSBH>' %(kpsq.GHF_NSRSBH if kpsq.GHF_NSRSBH is not None else '') #购货方识别号
    inXml += '<GMFMC>%s</GMFMC>' %escapeXmlField(kpsq.GHFMC) #购货方名称
    inXml += '<LZFPPZ>%s</LZFPPZ>' %kpsq.InfoKind #蓝字发票票种
    inXml += '<LZSFZZFP>N</LZSFZZFP>'#蓝字是否纸质发票Y：纸质发票；N：电子发票
    inXml += '<LZKPRQ>%s</LZKPRQ>' %kpsq.YFP_KPRQ.strftime('%Y-%m-%d %H:%M:%S')#蓝字开票日期yyyy-MM-dd HH:mm:ss
    inXml += '<LZFPDM>%s</LZFPDM>' %(kpsq.YFP_DM if kpsq.YFP_DM else '') #原发票代码
    inXml += '<LZFPHM>%s</LZFPHM>' %(kpsq.YFP_HM if kpsq.YFP_HM else '') #原发票号码
    inXml += '<LZFPJE>%.02f</LZFPJE>' %kpsq.HJBHSJE #合计不含税金额
    
    inXml += '<LZFPSE>%.02f</LZFPSE>' %kpsq.HJSE #合计税额
    inXml += '<LZFPTDYSLXDM></LZFPTDYSLXDM>' #蓝字发票特定要素类型代码01：成品油；02：稀土发票；03：建筑服务发票；04：货物运输服务发票；05：不动产销售服务发票；06：不动产租赁服务发票；07：代收车船税；08：通行费；09：旅客运输服务发票；10：医疗服务（住院）发票；11：医疗服务（门诊）发票；12：自产农产品销售发票；13：拖拉机和联合收割机发票；14：机动车；15：二手车；16：农产品收购发票；17：光伏收购发票；18：卷烟发票
    inXml += '<CHYY>1</CHYY>' #冲红原因1：开票有误；2：销货退回；3：服务终止；4：销售转让
    inXml += '<HZCXJE>%.02f</HZCXJE>' %kpsq.HJBHSJE #红字冲销金额
    inXml += '<HZCXSE>%.02f</HZCXSE>' %kpsq.HJSE #红字冲销税额
    
    
    kpsqGoodsCnt = 0
    for kpsqGoods in kpsqGoodss:
        if kpsqGoods.XMJE == 0:
            continue
        kpsqGoodsCnt += 1
    
    inXml += '<QDHZSQ_XMXXS class="QDHZSQ_XMXX;" size="%s">' %kpsqGoodsCnt
    for idx,kpsqGoods in enumerate(kpsqGoodss):
        if kpsqGoods.XMJE == 0:
            continue
        inXml += '<QDHZSQ_XMXX>'
        inXml += '<HH>%s</HH>' %(idx+1) #行号
        inXml += '<LZFPHH>%s</LZFPHH>' %(idx+1)#蓝字发票行号
        inXml += '<XMMC>%s</XMMC>' %escapeXmlField(kpsqGoods.XMMC) #项目名称
        
        inXml += '<GGXH>%s</GGXH>' %escapeXmlField(kpsqGoods.GGXH) #规格型号
        
        inXml += '<DW>%s</DW>' %escapeXmlField(kpsqGoods.XMDW) #项目单位
        inXml += '<SL>%s</SL>' %(kpsqGoods.XMSL if kpsqGoods.XMSL is not None else '') #项目数量
        
        inXml += '<DJ>%s</DJ>' %(kpsqGoods.XMDJ_HS if kpsqGoods.XMDJ_HS is not None else '')  #项目单价
        inXml += '<JE>%s</JE>' %round(kpsqGoods.XMJE_HS, 2) #项目金额
        inXml += '<SE>%s</SE>' %(kpsqGoods.SE if kpsqGoods.SE is not None else '') #税额
        inXml += '<SLV>%s</SLV>' %kpsqGoods.SL #税率
        inXml += '<SPFWSSFLBM>%s</SPFWSSFLBM>' %kpsqGoods.SPBM #商品编码
        inXml += '</QDHZSQ_XMXX>'
     

    inXml += '</QDHZSQ_XMXXS>'
    inXml += '''</REQUEST_QDHZSQ>'''
    if BDCZL:
        inXml += '<FPKJXX_BDCZL class="FPKJXX_BDCZL">'
        inXml += '<BDCZLDZ>%s</BDCZLDZ>' %escapeXmlField(kpsq.BDCZLDZ)
        inXml += '<ZLQQ>%s</ZLQQ>' %escapeXmlField(kpsq.ZLQQ)
        inXml += '<ZLQZ>%s</ZLQZ>' %escapeXmlField(kpsq.ZLQZ)
        inXml += '<KDSBZ>%s</KDSBZ>' %escapeXmlField(kpsq.KDSBZ)
        inXml += '<CQZSBH>%s</CQZSBH>' %escapeXmlField(kpsq.CQZSBH)
        inXml += '<DW>%s</DW>' %escapeXmlField(kpsq.DW)
        inXml += '</FPKJXX_BDCZL>'
    inXml += '''</QDHZSQ_XMXXS>''' 

    
        
        
        
    inXml = inXml.encode('gbk', 'ignore').decode('gbk', 'ignore')
    
    return inXml
    
def callHangtianApi(app, inXmlInner, inXmlWithOuter):

    #begin = time.time()
    
    response = requests.post(
        url = app.HANGTIAN_URL.strip(),
        data = inXmlWithOuter,
        timeout=60,
        headers={'Connection':'close'}
    )
    outXml = response.text
        
    if not outXml:
        raise NameError('返回报文为空！')

    return outXml  
        
def fpkjInterface(app, kpsq):
    try:
        if kpsq.fpkjApplyDate is None:
            kpsq.fpkjApplyDate = datetime.datetime.now()
            kpsq.save()
        callcode = 'ECXML.FPKJ.BC.E_INV'
        inXml = fpkjInxml(app, kpsq)
        
        inXmlInner = inXml
                
        passWord = getPassWord(app.passWord) #调用航天服务的用户密码
        des3Key = str(app.des3Key) if app.des3Key else passWord[len(passWord)-24:]
        des = DES3.new(des3Key, DES3.MODE_ECB) # 创建一个DES实例        
        inXml = des.encrypt(pad(inXml))
        
        ############################1.发票开具######################
        #1.1.制作外层报文
        inXmlWithOuter = makeOuterXml(app, data={
            'interfaceCode':callcode,
            'passWord':'' if app.des3Key else passWord,
            'content' : base64.b64encode(inXml).decode('utf-8')
        }, pretty=0)
        
        #1.2.调用航天接口开具发票
        outXml = callHangtianApi(app, inXmlInner, inXmlWithOuter)

        doc = parseString(outXml)
        interface = doc.getElementsByTagName("interface")[0]
        globalInfo = interface.getElementsByTagName("globalInfo")[0]
        returnStateInfo = interface.getElementsByTagName("returnStateInfo")[0]
        returnCode = getText(returnStateInfo, 'returnCode')
        returnMessage = base64.b64decode(getText(returnStateInfo, 'returnMessage'))
        return {
            'returnCode':returnCode,
            'returnMessage':returnMessage.decode('utf8'),
        }
        
    except Exception as e:
        return {
            'returnCode':'9999',
            'returnMessage':'%s' %e,
        }

######################################################
#发票查询
######################################################
def fpcxInterface(app, kpsq, WJLX='PDF'):
    if int(kpsq.InfoKind) in [61, 62,51] and app.userName:
        return fpcxInterface_quandian(app, kpsq, WJLX)
    else:
        return fpcxInterface_old(app, kpsq)

def fpcxInterface_old(app, kpsq):
    FPQQLSH = kpsq.FPQQLSH
    returnCode = '9999'
    returnMessage = ''
    content = ''
    try:
        ############################2.发票下载######################
        #2.1.制作完整的内层报文
        inXml = u'''<REQUEST_FPXXXZ_NEW class="REQUEST_FPXXXZ_NEW"> 
 <FPQQLSH>%s</FPQQLSH> 
 <DSPTBM>%s</DSPTBM> 
 <NSRSBH>%s</NSRSBH> 
 <DDH>DDH</DDH>
 <PDF_XZFS>2</PDF_XZFS> 
</REQUEST_FPXXXZ_NEW> 
''' %(FPQQLSH.rjust(20,'0'), app.userName, kpsq.XHF_NSRSBH)
        inXmlInner = inXml
        
        passWord = getPassWord(app.passWord) #调用航天服务的用户密码
        des3Key = str(app.des3Key) if app.des3Key else passWord[len(passWord)-24:]
        des = DES3.new(des3Key, DES3.MODE_ECB) # 创建一个DES实例        
        inXml = des.encrypt(pad(inXml))
        
        
        inXmlWithOuter = makeOuterXml(app, data={
            'interfaceCode':'ECXML.FPXZ.CX.E_INV',
            'passWord':'' if app.des3Key else passWord,
            'content' : base64.b64encode(inXml).decode('utf-8')
        }, pretty=0)
        
        #2.2.调用航天接口下载发票
        outXml = callHangtianApi(app, inXmlInner, inXmlWithOuter)
        if not outXml:
            return {
                'returnCode':'9401',
                'returnMessage':'开票处理中',
            }
        
        #2.3解析返回
        doc = parseString(outXml)
        interface = doc.getElementsByTagName("interface")[0]
        globalInfo = interface.getElementsByTagName("globalInfo")[0]
        returnStateInfo = interface.getElementsByTagName("returnStateInfo")[0]
        returnCode = getText(returnStateInfo, 'returnCode') #外层报文returnCode
        returnMessage = base64.b64decode(getText(returnStateInfo, 'returnMessage'))
        if returnCode == '9602':
            return {
                'returnCode':'9401',
                'returnMessage':'开票处理中',
            }

        if returnCode!='0000':
            return {
                'returnCode':returnCode,
                'returnMessage':returnMessage.decode('utf8'),
            }
        
        #外层报文成功，则解析内层报文content
        Data = interface.getElementsByTagName("Data")[0]
        content = getText(Data, 'content')
        content = base64.b64decode(content)#先base64解码
        content = unpad(des.decrypt(content)) # 解密
        
        doc = parseString(content) #内层报文XML
        REQUEST_FPKJXX_FPJGXX_NEW = doc.getElementsByTagName("REQUEST_FPKJXX_FPJGXX_NEW")[0]
        RETURNCODE = getText(REQUEST_FPKJXX_FPJGXX_NEW, 'RETURNCODE')  #外层报文RETURNCODE
        RETURNMESSAGE = getText(REQUEST_FPKJXX_FPJGXX_NEW, 'RETURNMESSAGE')
        #print FPQQLSH, 'RETURNCODE=',RETURNCODE,'RETURNMESSAGE=',RETURNMESSAGE
        if RETURNCODE!='0000':
            return {
                'returnCode':RETURNCODE,
                'returnMessage':RETURNMESSAGE.decode('utf8'),
            }
        return {
            'returnCode':RETURNCODE,
            'returnMessage':RETURNMESSAGE.decode('utf8'),
            'KPRQ':datetime.datetime.strptime(getText(REQUEST_FPKJXX_FPJGXX_NEW, 'KPRQ'), '%Y%m%d%H%M%S'),
            'FP_DM':getText(REQUEST_FPKJXX_FPJGXX_NEW, 'FP_DM'),
            'FP_HM':getText(REQUEST_FPKJXX_FPJGXX_NEW, 'FP_HM'),
            'PDF_URL':getText(REQUEST_FPKJXX_FPJGXX_NEW, 'PDF_URL'),
            'FWM':getText(REQUEST_FPKJXX_FPJGXX_NEW, 'FWM'),
        }
    
    except Exception as e:
        return {
            'returnCode':'9999',
            'returnMessage':'%s' %e,
        }

            
def fpcxInterface_quandian(app, kpsq, WJLX):
    FPQQLSH = kpsq.FPQQLSH
    returnCode = '9999'
    returnMessage = ''
    content = ''
    try:
        ############################2.发票下载######################
        #2.1.制作完整的内层报文
        inXml = u'''<REQUEST_WJXZ class="REQUEST_WJXZ"> 
    <FPQQLSH>%s</FPQQLSH> 
    <DSPTBM>%s</DSPTBM> 
    <NSRSBH>%s</NSRSBH> 
    <XZFS>1</XZFS>
    <WJLX>%s</WJLX>
    </REQUEST_WJXZ> 
    ''' %(FPQQLSH.rjust(20,'0'), app.userName, kpsq.XHF_NSRSBH,WJLX)
        
        inXmlInner = inXml
        #print u'--------------发票查询 inXml--------------'
        #print inXml
        
        #logging.info('------------------------发票查询------------------------')
        #logging.info('内层报文')
        #logging.info(inXml)
        
        #print '------------------------inXml encrypt-------------------------'
        
        passWord = getPassWord(app.passWord) #调用航天服务的用户密码
        des3Key = str(app.des3Key) if app.des3Key else passWord[len(passWord)-24:]
        des = DES3.new(des3Key, DES3.MODE_ECB) # 创建一个DES实例        
        inXml = des.encrypt(pad(inXml))
        
        #print('ptbm:', app.userName)
        inXmlWithOuter = makeOuterXml(app, data={
            'interfaceCode':'ECXML.WJXZ.CX.E_INV',
            'passWord':'' if app.des3Key else passWord,
            'content' : base64.b64encode(inXml).decode('utf-8')
        }, pretty=0)
        #print(inXmlWithOuter)
        #2.2.调用航天接口下载发票
        outXml = callHangtianApi(app, inXmlInner, inXmlWithOuter)
        #print(outXml)
        if not outXml:
            return {
                'returnCode':'9401',
                'returnMessage':'开票处理中',
            }
        #fw = open('c:\\quandian2.xml', 'wb')    
        #fw.write(outXml)
        #fw.close()
        #return
        
        #2.3解析返回
        doc = parseString(outXml)
        interface = doc.getElementsByTagName("interface")[0]
        globalInfo = interface.getElementsByTagName("globalInfo")[0]
        returnStateInfo = interface.getElementsByTagName("returnStateInfo")[0]
        returnCode = getText(returnStateInfo, 'returnCode') #外层报文returnCode
        returnMessage = base64.b64decode(getText(returnStateInfo, 'returnMessage'))
        #print('------------------------fpcxInterface------------------------')
        #print('returnCode=',returnCode, returnMessage.decode('utf8'))
        if returnCode!='0000':
            return {
                'returnCode':returnCode,
                'returnMessage':returnMessage.decode('utf8'),
            }
        #外层报文成功，则解析内层报文content
        Data = interface.getElementsByTagName("Data")[0]
        #print(Data)
        dataDescription = Data.getElementsByTagName("dataDescription")[0]
        zipCode = getText(dataDescription, 'zipCode') 
        encryptCode = getText(dataDescription, 'encryptCode')
        codeType = getText(dataDescription, 'codeType')
        content = getText(Data, 'content')
        content = base64.b64decode(content)#先base64解码
        #print(StringIO(content))
        if zipCode == '1':
            #content = gzip.GzipFile(fileobj=content)
            content = gzip.decompress(content)
        #print(content)

        #print(type(des.decrypt(content).decode('utf-8')))
        if encryptCode == '1' and codeType=='3DES':
            content = unpad(des.decrypt(content).decode('utf-8')) # 解密
        #print(content)
            #fw = open('c:\\quandian_decript.xml', 'wb')    
            #fw.write(content)
            #fw.close()
        #if WJLX == 'OFD':
        #    print(content)
        doc = parseString(content) #内层报文XML
        REQUEST_FPKJXX_FPJGXX_NEW = doc.getElementsByTagName("RESPONSE_WJXZ")[0]
        #print(REQUEST_FPKJXX_FPJGXX_NEW.getElementsByTagName("RETURNCODE")[0].childNodes[0])
        RETURNCODE = getText(REQUEST_FPKJXX_FPJGXX_NEW, 'RETURNCODE')  #外层报文RETURNCODE
        RETURNMESSAGE = getText(REQUEST_FPKJXX_FPJGXX_NEW, 'RETURNMESSAGE')
        #print(FPQQLSH, 'RETURNCODE=',RETURNCODE,'RETURNMESSAGE=',RETURNMESSAGE)
        #print(getText(REQUEST_FPKJXX_FPJGXX_NEW, 'KPRQ'))
        if RETURNCODE!='0000':
            return {
                'returnCode':RETURNCODE,
                'returnMessage':RETURNMESSAGE,
            }
        else:
            if WJLX == 'PDF':
                KPRQ = datetime.datetime.strptime(getText(REQUEST_FPKJXX_FPJGXX_NEW, 'KPRQ'), '%Y-%m-%d %H:%M:%S')
                FP_HM = getText(REQUEST_FPKJXX_FPJGXX_NEW, 'FP_HM')
                PDF_URL = getText(REQUEST_FPKJXX_FPJGXX_NEW, 'URL')
                return {
                    'returnCode':RETURNCODE,
                    'returnMessage':RETURNMESSAGE,
                    'KPRQ':KPRQ,
                    'FP_DM':getText(REQUEST_FPKJXX_FPJGXX_NEW, 'FP_DM'),
                    'FP_HM':FP_HM,
                    'PDF_URL':PDF_URL,
                    'FWM':getText(REQUEST_FPKJXX_FPJGXX_NEW, 'FWM'),
                }
            elif WJLX == 'OFD':
                KPRQ = datetime.datetime.strptime(getText(REQUEST_FPKJXX_FPJGXX_NEW, 'KPRQ'), '%Y-%m-%d %H:%M:%S')
                FP_HM = getText(REQUEST_FPKJXX_FPJGXX_NEW, 'FP_HM')
                #print('OFD')
                OFD_URL = getText(REQUEST_FPKJXX_FPJGXX_NEW, 'URL')
                OFD_FILE = getText(REQUEST_FPKJXX_FPJGXX_NEW, 'FILE')
                if OFD_FILE in [None, ''] and OFD_FILE:
                    #OFD文件流
                    OFD_FILE = base64.b64decode(OFD_FILE)
                res = {
                    'returnCode':RETURNCODE,
                    'returnMessage':RETURNMESSAGE,
                    'KPRQ':KPRQ,
                    'FP_DM':getText(REQUEST_FPKJXX_FPJGXX_NEW, 'FP_DM'),
                    'FP_HM':FP_HM,
                    'OFD_URL':OFD_URL,
                    'FWM':getText(REQUEST_FPKJXX_FPJGXX_NEW, 'FWM'),
                }
                #print(res)
                return res
            elif WJLX == 'XML':
                KPRQ = datetime.datetime.strptime(getText(REQUEST_FPKJXX_FPJGXX_NEW, 'KPRQ'), '%Y-%m-%d %H:%M:%S')
                FP_HM = getText(REQUEST_FPKJXX_FPJGXX_NEW, 'FP_HM')
                #print('OFD')
                XML_URL = getText(REQUEST_FPKJXX_FPJGXX_NEW, 'URL')
                res = {
                    'returnCode':RETURNCODE,
                    'returnMessage':RETURNMESSAGE,
                    'KPRQ':KPRQ,
                    'FP_DM':getText(REQUEST_FPKJXX_FPJGXX_NEW, 'FP_DM'),
                    'FP_HM':FP_HM,
                    'XML_URL':XML_URL,
                    'FWM':getText(REQUEST_FPKJXX_FPJGXX_NEW, 'FWM'),
                }
                #print(res)
                return res
        
    except Exception as e:
        return {
            'returnCode':'9999',
            'returnMessage':'%s' %e,
        }

######################################################
#可用发票数量查询
######################################################
def kyfpslInterface(app):
    returnCode = '9999'
    returnMessage = ''
    content = ''
    try:
        ############################3.可用发票数量查询######################
        #3.1.制作完整的内层报文
        inXml = u'''<REQUEST_KYFPSL class="REQUEST_KYFPSL">
<NSRSBH>%s</NSRSBH>
</REQUEST_KYFPSL>
''' %(app.XHF_NSRSBH)
        #print '--------------inXml--------------'
        #print inXml
        inXmlInner = inXml
        
        #logging.info('------------------------可用发票数量查询------------------------')
        #logging.info('内层报文')
        #logging.info(inXml)
        
        #print '------------------------inXml encrypt-------------------------'
        passWord = getPassWord(app.passWord) #调用航天服务的用户密码
        des3Key = str(app.des3Key) if app.des3Key else passWord[len(passWord)-24:]
        des = DES3.new(des3Key, DES3.MODE_ECB) # 创建一个DES实例        
        inXml = des.encrypt(pad(inXml))
        
        
        inXmlWithOuter = makeOuterXml(app, data={
            'interfaceCode':'ECXML.QY.KYFPSL',
            'passWord':'' if app.des3Key else passWord,
            'content' : base64.b64encode(inXml).decode('utf-8')
        }, pretty=0)
        
        #3.2.调用航天接口
        outXml = callHangtianApi(app, inXmlInner, inXmlWithOuter)
        if not outXml:
            return {
                'returnCode':'9999',
                'returnMessage':'未知的错误',
            }
        
        #3.3解析返回
        doc = parseString(outXml)
        interface = doc.getElementsByTagName("interface")[0]
        globalInfo = interface.getElementsByTagName("globalInfo")[0]
        returnStateInfo = interface.getElementsByTagName("returnStateInfo")[0]
        returnCode = getText(returnStateInfo, 'returnCode') #外层报文returnCode
        returnMessage = base64.b64decode(getText(returnStateInfo, 'returnMessage'))
        #print '------------------------kyfpslInterface------------------------'
        #print 'returnCode=',returnCode, returnMessage.decode('utf8')
        if returnCode!='0000':
            return {
                'returnCode':returnCode,
                'returnMessage':returnMessage.decode('utf8'),
            }
        
        #外层报文成功，则解析内层报文content
        Data = interface.getElementsByTagName("Data")[0]
        content = getText(Data, 'content')
        content = base64.b64decode(content)#先base64解码
        content = unpad(des.decrypt(content)) # 解密
        
        #print content
        doc = parseString(content) #内层报文XML
        RESPONSE_KYFPSL = doc.getElementsByTagName("RESPONSE_KYFPSL")[0]
        return {
            'returnCode':'0000',
            'KYFPSL':getText(RESPONSE_KYFPSL, 'KYFPSL'),
            'TJSJ':datetime.datetime.strptime(getText(RESPONSE_KYFPSL, 'TJSJ'), '%Y-%m-%d %H:%M:%S'),
        }
    except Exception as e:
        return {
            'returnCode':'9999',
            'returnMessage':'%s' %e,
        }    
        
def getQrCodeInterface(app,queryType):
    returnCode = '9999'
    returnMessage = ''
    content = ''
    try:
        ############################3.可用发票数量查询######################
        #3.1.制作完整的内层报文
        inXml = u'''<REQUEST_QDEWM_GET class="REQUEST_QDEWM_GET">
    <SQLSH>%s</SQLSH>
    <KPLX>2</KPLX>
</REQUEST_QDEWM_GET>
''' %str(uuid.uuid4().hex)
        #print '--------------inXml--------------'
        #print inXml
        inXmlInner = inXml
        
        #logging.info('------------------------可用发票数量查询------------------------')
        #logging.info('内层报文')
        #logging.info(inXml)
        
        #print '------------------------inXml encrypt-------------------------'
        passWord = getPassWord(app.passWord) #调用航天服务的用户密码
        des3Key = str(app.des3Key) if app.des3Key else passWord[len(passWord)-24:]
        des = DES3.new(des3Key, DES3.MODE_ECB) # 创建一个DES实例        
        inXml = des.encrypt(pad(inXml))
        
        
        inXmlWithOuter = makeOuterXml(app, data={
            'interfaceCode':'ECXML.QDEWM.GET',
            'passWord':'' if app.des3Key else passWord,
            'content' : base64.b64encode(inXml).decode('utf-8')
        }, pretty=0)
        
        #3.2.调用航天接口
        outXml = callHangtianApi(app, inXmlInner, inXmlWithOuter)
        if not outXml:
            return {
                'returnCode':'9999',
                'returnMessage':'未知的错误',
            }
        
        #3.3解析返回
        doc = parseString(outXml)
        interface = doc.getElementsByTagName("interface")[0]
        globalInfo = interface.getElementsByTagName("globalInfo")[0]
        returnStateInfo = interface.getElementsByTagName("returnStateInfo")[0]
        returnCode = getText(returnStateInfo, 'returnCode') #外层报文returnCode
        returnMessage = base64.b64decode(getText(returnStateInfo, 'returnMessage'))
        #print '------------------------kyfpslInterface------------------------'
        #print 'returnCode=',returnCode, returnMessage.decode('utf8')
        if returnCode!='0000':
            return {
                'returnCode':returnCode,
                'returnMessage':returnMessage.decode('utf8'),
            }
        
        #外层报文成功，则解析内层报文content
        Data = interface.getElementsByTagName("Data")[0]
        content = getText(Data, 'content')
        content = base64.b64decode(content)#先base64解码
        content = unpad(des.decrypt(content)) # 解密
        
        print(content)
        doc = parseString(content) #内层报文XML
        RESPONSE_QDEWM_GET = doc.getElementsByTagName("RESPONSE_QDEWM_GET")[0]
        return {
            'returnCode':'0000',
            'imgBase64':getText(RESPONSE_QDEWM_GET, 'EWM'),
            
        }
    except Exception as e:
        return {
            'returnCode':'9999',
            'returnMessage':'%s' %e,
        }
        
def smartcodeInterface(app):
    #returnCode = '9999'
    #returnMessage = ''
    #content = ''
    #try:
        ############################3.可用发票数量查询######################
        #3.1.制作完整的内层报文
    inXml = u'''<REQUEST_SMARTCODE class="REQUEST_SMARTCODE">
<XMMC>马克杯</XMMC>
</REQUEST_SMARTCODE>
'''
    inXmlInner = inXml
    
    #logging.info('------------------------可用发票数量查询------------------------')
    #logging.info('内层报文')
    #logging.info(inXml)
    
    #print '------------------------inXml encrypt-------------------------'
    passWord = getPassWord(app.passWord) #调用航天服务的用户密码
    print(passWord)
    des3Key = str(app.des3Key) if app.des3Key else passWord[len(passWord)-24:]
    des = DES3.new(des3Key, DES3.MODE_ECB) # 创建一个DES实例        
    inXml = des.encrypt(pad(inXml))
    
    
    inXmlWithOuter = makeOuterXml(app, data={
        'interfaceCode':'ECXML_SMARTCODE',
        'passWord':'' if app.des3Key else passWord,
        'content' : base64.b64encode(inXml).decode('utf-8')
    }, pretty=0)
    
    #3.2.调用航天接口
    outXml = callHangtianApi(app, inXmlInner, inXmlWithOuter)
    print(outXml)
    if not outXml:
        return {
            'returnCode':'9999',
            'returnMessage':'未知的错误',
        }
    
    #3.3解析返回
    doc = parseString(outXml)
    interface = doc.getElementsByTagName("interface")[0]
    globalInfo = interface.getElementsByTagName("globalInfo")[0]
    returnStateInfo = interface.getElementsByTagName("returnStateInfo")[0]
    returnCode = getText(returnStateInfo, 'returnCode') #外层报文returnCode
    returnMessage = base64.b64decode(getText(returnStateInfo, 'returnMessage'))
    if returnCode!='0000':
        return {
            'returnCode':returnCode,
            'returnMessage':returnMessage.decode('utf8'),
        }
    
    #外层报文成功，则解析内层报文content
    Data = interface.getElementsByTagName("Data")[0]
    content = getText(Data, 'content')
    content = base64.b64decode(content)#先base64解码
    content = unpad(des.decrypt(content)) # 解密
    
    doc = parseString(content) #内层报文XML
    RESPONSE_QDEWM_GET = doc.getElementsByTagName("RESPONSE_QDEWM_GET")[0]
    return {
        'returnCode':'0000',
        'imgBase64':getText(RESPONSE_QDEWM_GET, 'EWM'),
        
    }

 
            